lightning address

2025-08-25 ยท 3 min read

A lightning address is an email-like string (ex: "philip@lexe.app") that lets users pay to a static human-readable identifier instead of a raw, single-use BOLT11 invoice.

payment flow #

  1. Payer wants to pay "philip@lexe.app".
  2. Payer client makes a request GET https://lexe.app/.well-known/lnurlp/philip.
  3. Server responds with an LNURLp JSON blob.
{
    "callback": "<callback>",
    "minSendable": 1,
    "maxSendable": 100000000,
    "metadata": "<metadata>",
    "commentAllowed": 200,
    "tag": "payRequest"
}

or

{
    "status": "ERROR",
    "reason": "No such user"
}
  1. Payer client follows LUD-06 (payRequest) flow.
  2. Payer client makes a request GET <callback><?|&>amount=<msats>.
  3. Server responds with a JSON blob:
{
    "pr": "<bolt11-invoice>",
    "routes": []
}

or

{
    "status": "ERROR",
    "reason": "Amount is too small"
}
  1. Payer client verifies the BOLT11 invoice:

    • invoice description_hash in the BOLT11 invoice matches SHA256(metadata).
    • invoice amount matches the requested amount (if any).
  2. Payer client pays the invoice.

details #

  • The metadata string must be hashed with SHA256 and included in the BOLT11 invoice's description_hash field.

  • Endpoints needs proper CORS headers so browsers will use them.

design notes (Cloudflare) #

problem #

We currently serve https://lexe.app from Cloudflare pages, but we need to somehow serve https://lexe.app/.well-known/lnurlp/* from our backend gateway service.

solution 1: redirect #

By far the simplest approach is to configure _redirects in our Cloudflare pages repo to redirect requests to /.well-known/lnurlp/* to our backend.

The main downside is that dumb clients that don't follow redirects will break.

solution 2: cloudflare workers reverse proxy #

Unfortunately, there's no origin rewrite rule available until Enterprise tier (>$2000/mo, "call me" pricing). The cloudflare-approved way to do this is to use a Cloudflare Worker function at that path that makes the request to our backend and returns the response.

Fortunately Worker CPU time limits don't apply to time spent waiting for fetch requests.

Pricing: Free: 100k requests/day, 10ms CPU time/request

solution 3: serve LNURLp blobs from Azure blob storage / Cloudflare R2 #

Cloudflare does support serving static files from various object storage providers (Cloudflare R2, AWS S3, Google Cloud Storage, Azure Blob Storage). Because the LNURLp blobs contain a "callback" URL that can point to our actual backend, we can just serve static JSON files from blob storage.

problem #

Our callback will have uncontrolled clients hitting our backend services, which means we need public webpki certs issued.